﻿using JetBrains.Annotations;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using WorkFlowCore.Authorization;
using WorkFlowCore.IRepositories;
using System.Linq.Dynamic.Core;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.Domain.Entities;
using WorkFlowCore.Framework.MultipleApp;

namespace WorkFlowCore.Framework.Repositories4EF
{
    public class BasicRepository4EF<TEntity, TKey> : EfCoreRepository<WorkflowDbContext, TEntity, TKey>, IBasicRepository<TEntity, TKey>, IReadOnlyBasicRepository<TEntity, TKey> where TEntity : class, IEntity<TKey>
    {
        private readonly IWorkflowSession session;

        public BasicRepository4EF(IWorkflowSession session,IDbContextProvider<WorkflowDbContext> dbContextProvider) :base(dbContextProvider)
        {
            this.session = session;
        }


        public async Task DeleteAsync(TKey id, bool autoSave = false, CancellationToken cancellationToken = default)
        {
            if ( cancellationToken.IsCancellationRequested) return;

            var entity = await (await GetDbContextAsync()).Set<TEntity>().FindAsync(id);
            if (entity != null)
            {
                (await GetDbContextAsync()).Remove<TEntity>(entity);
                if (autoSave) await (await GetDbContextAsync()).SaveChangesAsync();
            }
        }

        public async Task DeleteAsync([global::JetBrains.Annotations.NotNullAttribute] TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default)
        {
            if ( cancellationToken.IsCancellationRequested) return;

            if (entity != null)
            {
                
                (await GetDbContextAsync()).Remove<TEntity>(entity);
                if (autoSave) await (await GetDbContextAsync()).SaveChangesAsync(cancellationToken);
            }
        }

        public async Task DeleteManyAsync([global::JetBrains.Annotations.NotNullAttribute] IEnumerable<TKey> ids, bool autoSave = false, CancellationToken cancellationToken = default)
        {

            if ( cancellationToken.IsCancellationRequested) return;

            var entities = (await GetDbContextAsync()).Set<TEntity>().Where(t => ids.Contains(t.Id)).ToList();
            if (entities != null)
            {
                
                (await GetDbContextAsync()).RemoveRange(entities);
                if (autoSave) await (await GetDbContextAsync()).SaveChangesAsync(cancellationToken);
            }

        }

        public async Task DeleteManyAsync([global::JetBrains.Annotations.NotNullAttribute] IEnumerable<TEntity> entities, bool autoSave = false, CancellationToken cancellationToken = default)
        {
            if ( cancellationToken.IsCancellationRequested) return;
            if (entities != null)
            {
                
                (await GetDbContextAsync()).RemoveRange(entities);
                if (autoSave) await (await GetDbContextAsync()).SaveChangesAsync(cancellationToken);
            }
        }

        public async Task DeleteManyAsync([NotNull] Expression<Func<TEntity, bool>> predicate, bool autoSave = false, CancellationToken cancellationToken = default)
        {
            if ( cancellationToken.IsCancellationRequested) return;

            var entities = (await GetDbContextAsync()).Set<TEntity>().WhereMultipleApp(session?.User?.AppId).Where(predicate).ToList();
            if (entities != null)
            {
                
                (await GetDbContextAsync()).RemoveRange(entities);
                if (autoSave) await (await GetDbContextAsync()).SaveChangesAsync(cancellationToken);
            }
        }

        public async Task<TEntity> FindAsync(TKey id, bool includeDetails = true, CancellationToken cancellationToken = default)
        {
            if ( cancellationToken.IsCancellationRequested) return null;
            return await (await GetDbContextAsync()).Set<TEntity>().FindAsync(new object[] { id }, cancellationToken);
        }

        public async Task<TEntity> GetAsync(TKey id, bool includeDetails = true, CancellationToken cancellationToken = default)
        {
            if ( cancellationToken.IsCancellationRequested) return null;
            return await (await GetDbContextAsync()).Set<TEntity>().FindAsync(new object[] { id }, cancellationToken);
        }

        public async Task<TEntity> GetAsync([NotNull] Expression<Func<TEntity, bool>> predicate, bool includeDetails = false, CancellationToken cancellationToken = default)
        {
            if ( cancellationToken.IsCancellationRequested) return null;
            return await Task.FromResult((await GetDbContextAsync()).Set<TEntity>().WhereMultipleApp(session?.User?.AppId).Where(predicate).FirstOrDefault());
        }

        public async Task<long> GetCountAsync(CancellationToken cancellationToken = default)
        {
            if ( cancellationToken.IsCancellationRequested) return 0;
            return await Task.FromResult((await GetDbContextAsync()).Set<TEntity>().WhereMultipleApp(session?.User?.AppId).Count());
        }

        public async Task<long> GetCountAsync([NotNull] Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default)
        {
            if ( cancellationToken.IsCancellationRequested) return 0;
            return await Task.FromResult((await GetDbContextAsync()).Set<TEntity>().WhereMultipleApp(session?.User?.AppId).Count(predicate));
        }

        public async Task<List<TEntity>> GetListAsync(bool includeDetails = false, CancellationToken cancellationToken = default)
        {
            if ( cancellationToken.IsCancellationRequested) return null;
            return await Task.FromResult((await GetDbContextAsync()).Set<TEntity>().WhereMultipleApp(session?.User?.AppId).ToList());
        }

        public async Task<List<TEntity>> GetListAsync([global::JetBrains.Annotations.NotNullAttribute] Expression<Func<TEntity, bool>> predicate, bool includeDetails = false, CancellationToken cancellationToken = default)
        {
            if ( cancellationToken.IsCancellationRequested) return null;
            return await Task.FromResult((await GetDbContextAsync()).Set<TEntity>().WhereMultipleApp(session?.User?.AppId).Where(predicate).ToList());
        }

        public async Task<List<TEntity>> GetPagedListAsync(int skipCount, int maxResultCount, string sorting, bool includeDetails = false, CancellationToken cancellationToken = default)
        {
            if ( cancellationToken.IsCancellationRequested) return null;
            var query = (await GetDbContextAsync()).Set<TEntity>().WhereMultipleApp(session?.User?.AppId).Where(t => true);
            if (!string.IsNullOrEmpty(sorting))
                query = query.OrderBy(sorting);
            return await Task.FromResult(query.Skip(skipCount).Take(maxResultCount).ToList());
        }

        public async Task<List<TEntity>> GetPagedListAsync([NotNull] Expression<Func<TEntity, bool>> predicate, int skipCount, int maxResultCount, string sorting, bool includeDetails = false, CancellationToken cancellationToken = default)
        {
            if ( cancellationToken.IsCancellationRequested) return null;
            var query = (await GetDbContextAsync()).Set<TEntity>().WhereMultipleApp(session?.User?.AppId).Where(predicate);
            if (!string.IsNullOrEmpty(sorting))
                query = query.OrderBy(sorting);
            return await Task.FromResult(query.Skip(skipCount).Take(maxResultCount).ToList());
        }

        public async Task<TEntity> InsertAsync([global::JetBrains.Annotations.NotNullAttribute] TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default)
        {
            if ( cancellationToken.IsCancellationRequested) return null;

            if (entity is IWithBaseInfoEntity<TKey>)
            {
                var withBaseInfoEntity = (IWithBaseInfoEntity<TKey>)entity;
                withBaseInfoEntity.CreationTime = DateTime.Now;
                //withBaseInfoEntity.CreatedUserId = session.User.Id;
                withBaseInfoEntity.ModifiedTime = DateTime.Now;
                withBaseInfoEntity.ModifiedUserId = session.User.Id;
                entity = (TEntity)withBaseInfoEntity;
            }

            entity.SetMultipleApp(session?.User?.AppId);

            
            await (await GetDbContextAsync()).Set<TEntity>().AddAsync(entity, cancellationToken);
            if (autoSave) await (await GetDbContextAsync()).SaveChangesAsync(cancellationToken);
            return await Task.FromResult(entity);
        }

        public async Task InsertManyAsync([global::JetBrains.Annotations.NotNullAttribute] IEnumerable<TEntity> entities, bool autoSave = false, CancellationToken cancellationToken = default)
        {
            if ( cancellationToken.IsCancellationRequested) return;
            foreach (var entity in entities)
            {
                if (entity is IWithBaseInfoEntity<TKey>)
                {
                    var withBaseInfoEntity = (IWithBaseInfoEntity<TKey>)entity;
                    withBaseInfoEntity.CreationTime = DateTime.Now;
                    //withBaseInfoEntity.CreatedUserId = session.User.Id;
                    withBaseInfoEntity.ModifiedTime = DateTime.Now;
                    withBaseInfoEntity.ModifiedUserId = session.User.Id;
                }
                entity.SetMultipleApp(session?.User?.AppId);
            }
            
            await (await GetDbContextAsync()).Set<TEntity>().AddRangeAsync(entities, cancellationToken);
            if (autoSave) await (await GetDbContextAsync()).SaveChangesAsync(cancellationToken);
        }

        public async Task<TEntity> UpdateAsync([global::JetBrains.Annotations.NotNullAttribute] TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default)
        {
            if (entity is IWithBaseInfoEntity<TKey>)
            {
                var withBaseInfoEntity = (IWithBaseInfoEntity<TKey>)entity;
                withBaseInfoEntity.ModifiedTime = DateTime.Now;
                withBaseInfoEntity.ModifiedUserId = session.User.Id;
            }
            entity.SetMultipleApp(session?.User?.AppId);
            (await GetDbContextAsync()).Set<TEntity>().Update(entity);
            return await Task.FromResult(entity);
        }

        public async Task UpdateManyAsync([global::JetBrains.Annotations.NotNullAttribute] IEnumerable<TEntity> entities, bool autoSave = false, CancellationToken cancellationToken = default)
        {
            if ( cancellationToken.IsCancellationRequested) return;
            foreach (var entity in entities)
            {
                if (entity is IWithBaseInfoEntity<TKey>)
                {
                    var withBaseInfoEntity = (IWithBaseInfoEntity<TKey>)entity;
                    withBaseInfoEntity.ModifiedTime = DateTime.Now;
                    withBaseInfoEntity.ModifiedUserId = session.User.Id;
                }
                entity.SetMultipleApp(session?.User?.AppId);
            }
            
            (await GetDbContextAsync()).Set<TEntity>().UpdateRange(entities);
            if (autoSave) await (await GetDbContextAsync()).SaveChangesAsync(cancellationToken);
        }
    }

    public class BasicRepository4EF<TEntity> : BasicRepository4EF<TEntity, Guid>, IReadOnlyBasicRepository<TEntity, Guid> where TEntity : class, IEntity<Guid>
    {
        public BasicRepository4EF(IWorkflowSession session, IDbContextProvider<WorkflowDbContext> dbContextProvider) : base(session, dbContextProvider)
        {
        }
    }
}

